<template>
  <el-input ref="inputRef" v-model.trim="currentValue" :clearable="clearable" v-bind="$attrs" :maxlength="maxLength"
    @input="handleInputEvent" @blur="handleBlurEvent">
    <div v-if="unit" ref="unitRef" slot="append">{{ unit }}</div>
  </el-input>
</template>

<script lang="ts">
import { defineComponent, ref, computed, nextTick } from "vue";
import { isLessThan } from "../../../utils/bigNumber";

export default defineComponent({
  name: "YInput",
  props: {
    modelValue: {
      type: [String, Number],
      default: "",
    },
    number: {
      type: Boolean,
      default: false,
    },
    integer: {
      type: Boolean,
      default: false,
    },
    integerDigit: {
      type: [Number, String],
      default: 10,
    },
    precision: {
      type: [Number, String],
      default: 2,
    },
    negative: {
      type: Boolean,
      default: false,
    },
    min: {
      type: [Number, String],
      default: 0,
    },
    max: {
      type: [Number, String],
      default: Infinity,
    },
    unit: {
      type: String,
      default: "",
    },
    clearable: {
      type: Boolean,
      default: true,
    },
    maxlength: {
      type: [Number, String],
      default: undefined,
    },
    maxLength: {
      type: [Number, String],
      default: undefined,
    },
  },
  emits: ["update:modelValue"],
  setup(props, { emit }) {
    const inputRef = ref<HTMLElement | null>(null);
    const unitRef = ref<HTMLElement | null>(null);
    const prevValue = ref<string | number>(props.modelValue);

    const maxLength = computed(() => {
      return props.maxlength || props.maxLength || (props as any)["max-length"];
    });

    const currentValue = computed({
      get: () => props.modelValue,
      set: (val) => handleInputValue(val),
    });

    // 使用局部变量来处理 precision
    const localPrecision = ref(Number(props.precision));

    const dealPrecision = () => {
      if (isNaN(localPrecision.value)) {
        localPrecision.value = 2;
      }
      if (localPrecision.value < 0) {
        localPrecision.value = 2;
      }
    };

    const handleBlurEvent = () => {
      nextTick(() => {
        if (props.min && isLessThan(currentValue.value, props.min)) {
          emit("update:modelValue", props.min);
          prevValue.value = props.min;
        } else if (
          Number.isFinite(Number(props.max)) &&
          isLessThan(props.max, currentValue.value)
        ) {
          emit("update:modelValue", props.max);
          prevValue.value = props.max;
        } else {
          emit("update:modelValue", currentValue.value);
          prevValue.value = currentValue.value;
        }
      });
    };

    const handleInputEvent = (val: string) => {
      let reg: RegExp | null = null;
      if (props.integer) {
        reg = new RegExp(`^(([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1}))$`);
        if (props.negative) {
          reg = new RegExp(`^(-?([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1})|(-))$`);
        }
      }
      if (props.number) {
        reg = new RegExp(
          `^(([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1}))([.](\\d{1,${localPrecision.value}}))?$`
        );
        if (props.negative) {
          reg = new RegExp(
            `^(-?([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1})|(-?))([.](\\d{1,${localPrecision.value}}))?$`
          );
        }
      }
      handleInputValue(val, reg);
    };

    const handleInputValue = (val: string | number, reg?: RegExp | null) => {
      const strVal = String(val);
      if (reg) {
        const matches = strVal.match(reg);
        if (matches) {
          prevValue.value = matches[0];
          emit("update:modelValue", matches[0]);
        } else {
          nextTick(() => {
            const intReg = props.negative
              ? new RegExp(`^(-?([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1}))[.]?$`)
              : new RegExp(`^(([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1}))[.]?$`);

            const floatReg = props.negative
              ? new RegExp(
                `^(-?([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1}))\\.?(\\d{1,${localPrecision.value}})?`
              )
              : new RegExp(
                `^(([1-9]{1}\\d{0,${Number(props.integerDigit) - 1}})|(0{1}))\\.?(\\d{1,${localPrecision.value}})?`
              );

            const matchesFloat = strVal.match(floatReg);
            const matchesInt = strVal.match(intReg);
            const numberReg = props.negative ? /^-?\d*$/ : /^\d*$/;

            if (matchesFloat) {
              if (numberReg.test(matchesFloat[0])) {
                if (matchesInt) {
                  emit("update:modelValue", matchesInt[0]);
                  prevValue.value = matchesInt[0];
                } else {
                  emit("update:modelValue", prevValue.value);
                }
              } else {
                if (props.number) {
                  emit("update:modelValue", matchesFloat[0]);
                  prevValue.value = matchesFloat[0];
                } else {
                  emit("update:modelValue", prevValue.value);
                }
              }
            } else {
              emit("update:modelValue", "");
            }
          });
        }
      } else {
        emit("update:modelValue", strVal);
      }
    };

    dealPrecision();

    return {
      inputRef,
      unitRef,
      currentValue,
      maxLength,
      handleInputEvent,
      handleBlurEvent,
    };
  },
});
</script>

<style lang="scss" scoped>
/* Your styles here */
</style>
